use std::fs::*;
use std::io::*;

const BUFFER_SIZE: u64 = 1024;
const CHAR_NEWLINE: char = '\n';

fn main() {
    let filepath = "bigfile.log";
    let tail_line = 10;
    let mut file = File::open(filepath).unwrap();
    let len = file.seek(SeekFrom::End(0)).unwrap_or(0);
    println!("File size：{:?} bytes", len);

    if len > BUFFER_SIZE {
        read_tail(file, tail_line, BUFFER_SIZE, 0);
    } else {
        read_all_lines(file);
    }

}

fn read_all_lines(file: File) {
    read_to_end(file, 0);
}

fn read_to_end(mut file: File, start_pos: u64) {
    let seek_result = file.seek(SeekFrom::Start(start_pos));
    if seek_result.is_err() {
        return;
    }
    let mut buf: String = String::new();
    let result = file.read_to_string(&mut buf);
    if result.is_ok() {
        println!("{}", buf);
    }
}

fn read_tail(mut file: File, tail_line: i32, tail_index: u64, read_lines: i32) {
    let seek_res = file.seek(SeekFrom::End(-(tail_index as i64)));
    if seek_res.is_err() {
        println!("Seek error: {:?}", seek_res);
        return;
    }
    
    let start_pos = seek_res.unwrap();
    println!("Seek to {:?}", start_pos);

    let mut buf = [0; BUFFER_SIZE as usize];
    let result = file.read(&mut buf);
    if result.is_err() {
        println!("Read error: {:?}", result);
        return;
    }
    
    buf.reverse();
    let buf_len = buf.len() as u64;
    let mut i = 0;
    let mut target_index = 0;
    let mut lines = read_lines;
    for b in buf {
        if i != 0 && b == CHAR_NEWLINE as u8 {
            lines += 1;
            if lines == tail_line {
                target_index = i;
                break;
            }
        }
        i += 1;
    }
    if lines == tail_line {
        let t_index = start_pos + buf_len - target_index;
        read_to_end(file, t_index);
    } else {
        let t_index = tail_index + BUFFER_SIZE;
        read_tail(file, tail_line, t_index, lines);
    }
}